/*******************************************************************************
***
***  Author: AngryPigFourier
***
***  Version: 0.0.1
***  Purpose: C-Utils string functions in C
***
*******************************************************************************/
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include "cu_string.h"

char* cu_string_toupper(char* s) {
    if (s == NULL || s[0] == '\0')
        return s;

    size_t i;
    for (i = 0; s[i] != '\0'; ++i)
        s[i] = toupper(s[i]);
    return s;
}

char* cu_string_tolower(char* s) {
    if (s == NULL || s[0] == '\0')
        return s;

    size_t i;
    for (i = 0; s[i] != '\0'; ++i)
        s[i] = tolower(s[i]);
    return s;
}

int cu_string_strcmp(const char *s1, const char *s2, int casesensitive) {
    if (s1 == s2)  // 指向同一片内存
        return 0;
    else if (s1 == NULL)
        return -1;
    else if (s2 == NULL)
        return 1;

    if (casesensitive) {
        return strcmp(s1, s2);
    }

    char* d1 = calloc(1, strlen(s1) + 1);
    char* d2 = calloc(1, strlen(s2) + 1);
    cu_string_duplicate(s1, d1, strlen(s1) + 1);
    cu_string_duplicate(s2, d2, strlen(s2) + 1);
    int res = strcmp(cu_string_toupper(d1), cu_string_toupper(d2));
    free(d1);
    free(d2);
    return res;
}

int cu_string_strncmp(const char *s1, const char *s2, size_t n, int casesensitive) {
    if (s1 == s2)  // 指向同一片内存
        return 0;
    else if (s1 == NULL)
        return -1;
    else if (s2 == NULL)
        return 1;

    if (casesensitive) {
        return strncmp(s1, s2, n);
    }

    char* d1 = calloc(1, strlen(s1) + 1);
    char* d2 = calloc(1, strlen(s2) + 1);
    cu_string_duplicate(s1, d1, strlen(s1) + 1);
    cu_string_duplicate(s2, d2, strlen(s2) + 1);
    int res = strncmp(cu_string_toupper(d1), cu_string_toupper(d2), n);
    free(d1);
    free(d2);
    return res;
}

char *cu_string_strcpy(char *dest, const char *src) {
    return strcpy(dest, src);
}

char *cu_string_strncpy(char *dest, const char *src, size_t n) {
    return strncpy(dest, src, n);
}

char *cu_string_strcat(char *dest, const char *src) {
    return strcat(dest, src);
}

char *cu_string_strncat(char *dest, const char *src, size_t n) {
    return strncat(dest, src, n);
}

size_t cu_string_duplicate(const char* s, char* d, size_t n) {
    if (s == NULL || d == NULL)
        return 0;

    // 不包含'\0'
    size_t len_s = strlen(s);
    size_t len = len_s > (n - 1) ? (n - 1) : len_s;
    strncpy(d, s, len);
    d[len] = '\0';
    return len;
}

char* cu_string_reverse(char* s) {
    if (s == NULL)
        return NULL;

    int i, j;
    for (i = 0, j = strlen(s) - 1; i < j; ++i, --j) {
        char tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
    }
    return s;
}


size_t cu_string_trim(char* s) {
    if (s == NULL || s[0] == '\0')
        return 0;

    size_t res = 0, start = 0; 
    size_t length = strlen(s), end = length-1;

    while (end >= 0 && isspace(s[end]) != 0) {
        s[end--] = '\0';
    }
    res += (length - 1 - end);

    while (start < end && isspace(s[start]) != 0){
        s[start++] = '\0';
    }

    res += start;

    if (start > 0) {
        size_t i = 0;
        while (start <= end) {
            s[i++] = s[start++];
        }
    }
    return res;
}

int cu_string_sprintf(char *str, const char *format, ...) {
    va_list args;
    va_start(args, format);
    size_t len = vsprintf(str, format, args);
    va_end(args);

    return len;
}

int cu_string_snprintf(char *str, size_t size, const char *format, ...) {
    va_list args;
    va_start(args, format);
    size_t len = vsnprintf(str, size, format, args);
    va_end(args);

    return len;
}

char* cu_string_replace_char(char* s, const char c, const char w){
    if (s == NULL)
        return NULL;

    size_t i = 0;
    while (s[i] != '\0') {
        if (s[i] == c) {
            s[i] = w;
        }
        ++i;
    }
    return s;
}

char* cu_string_replace_chars(char* s, const char* c, const char w) {
    if (s == NULL || c == NULL)
        return s;

    size_t i = 0, c_len = strlen(c);
    while (s[i] != '\0') {
        size_t j;
        for (j = 0; j < c_len; ++j) {
            if (s[i] == c[j]) {
                s[i] = w;
                break;
            }
        }

        ++i;
    }
    return s;
}

char* cu_string_remove_char(char* s, const char c){
    if (s == NULL)
        return NULL;

    size_t i = 0, j = 0;
    while (s[j] != '\0') {
        if (s[j] == c) {
            ++j;
            continue;
        }

        s[i] = s[j];
        ++i;
        ++j;
    }
    s[i] = '\0';
    return s;
}

char* cu_string_remove_chars(char* s, const char* c) {
    if (s == NULL || c == NULL)
        return s;

    size_t i = 0, j = 0, c_len = strlen(c);
    while (s[j] != '\0') {
        int is_same = 0;
        size_t q;
        for (q = 0; q < c_len; ++q) {
            if (s[j] == c[q]) {
                is_same = 1;
                break;
            }
        }
        if(is_same) {
            ++j;
            continue;
        }

        s[i] = s[j];
        ++i;
        ++j;
    }
    s[i] = '\0';
    return s;
}

char* cu_string_strchr(const char* s, const char c) {
    if (s == NULL) {
        return NULL;
    }

    return strchr((char*)s, c);
}

char* cu_string_strrchr(const char* s, const char c) {
    if (s == NULL) {
        return NULL;
    }

    return strrchr((char*)s, c);
}

int cu_string_find_char(const char* s, const char c) {
    if (s == NULL) {
        return -1;
    }
        
    char* pos = cu_string_strchr(s, c);
    if (pos == NULL) {
        return -1;
    }
    return pos - s;
}

int cu_string_find_char_r(const char* s, const char c) {
    if (s == NULL) {
        return -1;
    }

    char* pos = cu_string_strrchr(s, c);
    if (pos == NULL) {
        return -1;
    }
    return pos - s;

}

char* cu_string_strstr(const char* s, const char* sub) {
    if (s == NULL || sub == NULL) {
        return (char*)s;
    }

    return strstr((char*)s, (char*)sub);
}

char* cu_string_strrstr(const char* s, const char* sub) {
    if (s == NULL || sub == NULL) {
        return (char*)s;
    }
    cu_string_reverse((char*)s);
    cu_string_reverse((char*)sub);

    char* ret = NULL;
    char* res = cu_string_strstr(s, sub);
    if(res != NULL) {
        int idx = (res + strlen(sub)) - (char* )s;
        ret = (char* )s + (strlen(s) - idx);
    }

    cu_string_reverse((char*)s);
    cu_string_reverse((char*)sub);
    return ret;
}

int cu_string_find_string(const char* s, const char* sub) {
    if (s == NULL || sub == NULL) {
        return -1;
    }
    char* pos = cu_string_strstr(s, sub);
    if (pos == NULL) {
        return -1;
    }
    return pos - s;
}

int cu_string_find_string_r(const char* s, const char* sub) {
    if (s == NULL || sub == NULL) {
        return -1;
    }
    char* pos = cu_string_strrstr(s, sub);
    if (pos == NULL) {
        return -1;
    }
    return pos - s;
}

int cu_string_find_any(const char* s, const char* s2) {
    if (s == NULL || s2 == NULL)
        return -1;

    char* loc = strpbrk((char*)s, s2);
    if (loc == NULL)
        return -1;
    return loc - s;
}

int cu_string_find_any_r(const char* s, const char* s2) {
    if (s == NULL || s2 == NULL)
        return -1;

    char* res = NULL;
    char* loc = strpbrk((char*)s, s2);
    if (loc == NULL)  
        return -1;

    while (loc != NULL) {
        res = loc;
        loc = strpbrk(loc + 1, s2);
    }
    return res - s;
}

int cu_string_split_char(const char* s, const char c, char** d, int num) {
    if (s == NULL || d == NULL || num <= 0)
        return 0;
    int cnt = 0;
    char * start = (char *)s;
    char * res = NULL;
    while(cnt < num) {
        char * res = cu_string_strchr(start, c);
        if(res != NULL) {
            cu_string_strncpy(d[cnt], start, res - start);
            cnt++;
            start = res + 1;
        }else {
            break;
        }
    }
    if(res == NULL && *start != '\0') {
        cu_string_strcpy(d[cnt++], start);
    }
    return cnt;
}

int cu_string_split_string(const char* s, const char* s2, char** d, int num) {
    if (s == NULL || d == NULL || num <= 0)
        return 0;
    int cnt = 0;
    if(s2 == NULL) {
        cu_string_strcpy(d[cnt++], s);
        return cnt;
    }
    char * start = (char *)s;
    char * res = NULL;
    while(cnt < num) {
        char * res = cu_string_strstr(start, s2);
        if(res != NULL) {
            cu_string_strncpy(d[cnt++], start, res - start);
            start = res + strlen(s2);
        }else {
            break;
        }
    }
    if(res == NULL && *start != '\0') {
        cu_string_strcpy(d[cnt++], start);
    }
    return cnt;
}
